
local jass = require 'jass.common'
local japi = require 'jass.japi'
local slk = require 'jass.slk'
local dbg = require 'jass.debug'
local rect = require 'types.rect'
local effect = require 'types.effect'
local damage = require 'types.damage'
local math = math
local table_insert = table.insert

---@class unit
local unit = class('unit')
local ignore_flag = false
local ac_game = ac.game
local last_summoned_unit
local gchash = 0

unit.level = 1                              --等级
unit.type = 'unit'                          --类型
unit.unit_type = 'unit'                     --单位类型
unit._is_alive = true                       --存活
unit.skill_points = 0                       --技能点
unit.skills = nil                           --技能
unit.damage_rate = 100                      --造成伤害的倍率(%)
unit.damaged_rate = 100                     --受到伤害的倍率(%)
unit.gold = 0                               --家当
unit.selected_radius = 16                   --选取半径
unit._timers = nil                          --单位计时器
unit.paused_clock = 0                       --已暂停时间
unit.last_pause_clock = 0                   --上一次暂停开始的时间
unit.proc = 1                               --系数
unit.user_data = nil                        --用户自定义数据
unit._last_point = nil                      --上一个位置
unit.high = 0                               --高度
unit.size = 1                               --大小
unit.default_size = nil                     --默认大小
unit.melee = nil                            --近战
unit.missile_art = nil                      --弹道
unit.missile_speed = nil                    --弹道速度
unit.script_order = false                   --命令
unit.pause_timer_count = 0                  --暂停定时器数量
unit.pause_skill_count = 0                  --暂停技能
unit.pause_buff_count = 0                   --暂停buff
unit.pause_mover_count = 0                  --暂停运动
unit.animation_properties = ''              --动画
unit.enemy_tag = nil                        --添加敌我识别
unit.active = true                          --战斗标记
unit.last_active_time = -99999              --上次战斗time
unit.wait = ac.uwait
unit.loop = ac.uloop
unit.timer = ac.utimer
unit.launch_distance = nil
unit.launch_angle = nil
unit.launch_z = nil
unit._class = nil
unit._class_timer = nil
unit.red = 100
unit.green = 100
unit.blue = 100
unit.alpha = 100

function unit:ctor(player, handle)
    self.id = base.id2string(jass.GetUnitTypeId(handle)) --id
    self.handle = handle                        --jass handle
    self.gchash = handle                        --gchash
    self.owner = player or g_playerMgr:GetPlayerByUnitHandle(handle)  --所有者
    self.born_point = self:get_point()          --出生点

    self:Init()
end

function unit:Init()
    --令物体可以飞行
    self:add_ability 'Arav'
    self:remove_ability 'Arav'

    --设置高度
    self:set_high(self:get_slk('moveHeight', 0))

    if self:getAbilityLevel 'Aloc' ~= 0 then
        self:set_class '马甲'
    end

    local data = ac.lni.unit[self:get_name()]
    if data then
        self.unit_type = data.type
        if data.attribute then
            for k, v in pairs(data.attribute) do
                self:set(k, v)
            end
        end
        if data.restriction then
            for _, v in ipairs(data.restriction) do
                self:add_restriction(v)
            end
        end
		if data.hero_skill then
			for _, skl in ipairs(data.hero_skill) do
				self:add_skill(skl, '英雄')
			end
		end
		if data.hide_skill then
			for _, skl in ipairs(data.hide_skill) do
				self:add_skill(skl, '隐藏')
			end
		end
	end

    --忽略警戒点
    --jass.RemoveGuardPosition(u.handle)
    --jass.SetUnitCreepGuard(u.handle, true)
end

function unit:__tostring()
    local player = self:get_owner()
    return ('%s|%s|%s'):format('unit', self:get_name(), player.base_name)
end

--region 事件
--注册单位事件
function unit:event(name)
    return ac.event_register(self, name)
end

--发起事件
function unit:event_dispatch(name, ...)
	local res = ac.event_dispatch(self, name, ...)
	if res ~= nil then
		return res
	end
	local player = self:get_owner()
	if player then
		local res = ac.event_dispatch(player, name, ...)
		if res ~= nil then
			return res
		end
	end
	local res = ac.event_dispatch(ac_game, name, ...)
	if res ~= nil then
		return res
	end
	return nil
end

function unit:event_notify(name, ...)
	ac.event_notify(self, name, ...)
	local player = self:get_owner()
	if player then
		ac.event_notify(player, name, ...)
	end
	ac.event_notify(ac_game, name, ...)
end
--endregion

--region id
--获得单位id
function unit:get_type_id()
	return self.id
end

function unit:is_type(type)
	return self.unit_type == type
end

--是否是英雄
function unit:is_hero()
	return self.unit_type == '英雄' and not self._is_illusion
end

function unit:is_illusion()
	return self._is_illusion
end

--是否是马甲单位
function unit:is_dummy()
	return self._is_dummy
end

--获得名字
function unit:get_name()
    return self.name or self:get_slk 'Propernames' or self:get_slk 'Name'
end
--endregion

--region 获取物编数据
--	数据项名称
--	[如果未找到,返回的默认值]
function unit:get_slk(name, default)
	local unit_data = slk.unit[self.id]
	if not unit_data then
		log.error('单位数据未找到', self.id)
		return default
	end
	local data = unit_data[name]
	if data == nil then
		return default
	end
	if type(default) == 'number' then
		return tonumber(data) or default
	end
	return data
end
--endregion

--region 自定义数据
function unit:set_data(key, value)
	if not self.user_data then
		self.user_data = {}
	end
	self.user_data[key] = value
end

--获取数据
--	索引
function unit:get_data(key)
	    if not self.user_data then
	        self.user_data = {}
	    end
		return self.user_data[key]
	end
--endregion

--region 死亡/删除
--杀死单位[致死伤害]
function unit:kill(killer)
    if not self:is_alive() then
        return false
    end

    if not killer then
        killer = self
    end

    self._is_alive = false
    self:set('生命', 0)

    if not self:is_dummy() then
        jass.KillUnit(self.handle)
        killer:event_notify('单位-杀死单位', killer, self)
        self:event_notify('单位-死亡', self, killer)
    end

    --打断施法
    self:cast_stop()
    --删除Buff
    if self.buffs then
        local buffs = {}
        for bff in pairs(self.buffs) do
            if not bff.keep then
                buffs[#buffs + 1] = bff
            end
        end
        for i = 1, #buffs do
            buffs[i]:remove()
        end
    end
    if self:is_illusion() then
        self:remove()
    elseif not self:is_hero() then
        self:wait_to_remove()
    end
    return true
end

--删除单位
function unit:remove()
    if self.removed then
        return
    end
    self.removed = true

    self._last_point = ac.point(jass.GetUnitX(self.handle), jass.GetUnitY(self.handle))
    self:event_notify('单位-移除', self)

    self:removeAllEffects()

    --移除单位的所有Buff
    if self.buffs then
        local buffs = {}
        for bff in pairs(self.buffs) do
            buffs[#buffs + 1] = bff
        end
        for i = 1, #buffs do
            buffs[i]:remove()
        end
    end

    --移除单位的所有技能
    for skill in self:each_skill() do
        skill:remove()
    end

    --移除单位身上的物品
    for i = 1, 6 do
        local it = self:find_skill(i, '物品')
        if it then
            it:remove()
        end
    end

    --移除单位身上的计时器
    if self._timers then
        for i, t in ipairs(self._timers) do
            t:remove()
        end
    end

    ignore_flag = true
    jass.RemoveUnit(self.handle)
    ignore_flag = false

    --删除单位
    g_unitMgr:remove(self)
end

--是否存活
function unit:is_alive()
    return not self.removed and self._is_alive
end

function unit:wait_to_remove()
    --将待删除的单位保存在1级表中
    table.insert(g_unitMgr.wait_to_remove_table1, self)
end
--endregion

--region 队伍
--获得单位的队伍
function unit:get_team()
    return self:get_owner():get_team()
end

--是否是友方
--	对象
function unit:is_ally(dest)
    return self:get_team() == dest:get_team()
end

--是否是敌人
--	对象
function unit:is_enemy(dest)
    return self:get_team() ~= dest:get_team()
end
--endregion

--region 位置
--获取位置
function unit:get_point()
    if self._dummy_point then
        return self._dummy_point
    end
    if self.removed then
        return self._last_point:copy()
    else
        return ac.point(jass.GetUnitX(self.handle), jass.GetUnitY(self.handle))
    end
end

--设置位置
function unit:setPoint(point)
    if self:has_restriction '禁锢' then
        return false
    end
    local x, y = point:get()
    jass.SetUnitX(self.handle, x)
    jass.SetUnitY(self.handle, y)
    if self._dummy_point then
        self._dummy_point[1] = x
        self._dummy_point[2] = y
    end
    return true
end

--移动单位到指定位置(检查碰撞)
--	移动目标
--	[无视地形阻挡]
--	[无视地图边界]
function unit:set_position(where, path, super)
    if self:has_restriction '禁锢' then
        return false
    end
    if where:get_point():is_block(path, super) then
        return false
    end
    local x, y = where:get_point():get()
    local x1, y1, x2, y2 = rect.map:get()
    if x < x1 then
        x = x1
    elseif x > x2 then
        x = x2
    end
    if y < y1 then
        y = y1
    elseif y > y2 then
        y = y2
    end
    self:setPoint(ac.point(x, y))
    return true
end

--传送到指定位置
--	[无视地形]
function unit:blink(target, path, not_stop)
    local source = self:get_point()
    if self:set_position(target, path) then
        self:event_notify('单位-传送完成', self, source, target)
    end
    if not not_stop then self:issue_order 'stop' end
end

--获取出生点
function unit:getBornPoint()
    return self.born_point
end
--endregion

--region 高度
--获取高度
--	[是否是绝对高度(地面高度+飞行高度)]
function unit:get_high(b)
    if b then
        return self:get_point():getZ() + self.high
    else
        return self.high
    end
end

--设置高度
--	高度
--	[是否是绝对高度]
function unit:set_high(high, b, change_time)
    if b then
        self.high = high - self:get_point():getZ()
    else
        self.high = high
    end
    if not self:has_restriction '阿卡林' then
        jass.SetUnitFlyHeight(self.handle, self.high, change_time or 0)
    end
end

--增加高度
--	高度
--	[是否是绝对高度]
function unit:add_high(high, b)
    self:set_high(self:get_high(b) + high)
end
--endregion

--region 朝向
--获得朝向
function unit:get_facing()
    if self._dummy_angle then
        return self._dummy_angle
    end
    return jass.GetUnitFacing(self.handle)
end

--设置朝向
--	朝向
--  瞬间转身
function unit:set_facing(angle, instant)
    if instant then
        japi.EXSetUnitFacing(self.handle, angle)
    else
        jass.SetUnitFacing(self.handle, angle)
    end
    if self._dummy_angle then
        self._dummy_angle = angle
    end
end
--endregion

--region 大小
--设置大小
--	大小
function unit:set_size(size)
    self.size = size
    if not self.default_size then
        self.default_size = tonumber(self:get_slk 'modelScale') or 1
    end
    size = size * self.default_size
    jass.SetUnitScale(self.handle, size, size, size)
end

--获取大小
function unit:get_size()
    return self.size
end

--增加大小
--	大小
function unit:addSize(size)
    size = size + self:get_size()
    self:set_size(size)
end
--endregion

--region 属性
--是否是近战
--	默认值
function unit:isMelee(default)
    if default then
        local art = self.missile_art or (self.weapon and self.weapon['弹道模型']) or self:get_slk 'Missileart_1'
        return not art or art == '' or art == '.mdl' or art == '.mdx'
    else
        if self.melee == nil then
            self.melee = self:isMelee(true)
        end

        return self.melee
    end
end

--设置单位是否是近战
--	近战
function unit:setMelee(b)
    self.melee = b
end

--获取技能的冷却(经过冷却缩减和冷却加速计算)
function unit:getSkillCool(cool)
    local cool = cool or 0
    --先计算冷却缩减
    cool = cool * (1 - self:get '冷却缩减' / 100)
    --再计算冷却加速
    local cs = self:get '攻击速度' / 2
    if cs > 0 then
        --每点加速视为技能频率加快1%
        cool = cool / (1 + cs / 100)
    elseif cs < 0 then
        --每点负加速视为技能间隔增加1%
        cool = cool * (1 - cs / 100)
    end
    return cool
end

--刷新所有技能的冷却
function unit:fresh_cool()
    local t = {}
    for skl in self:each_skill() do
        local f = skl:fresh_cool()
        table.insert(t, f)
    end
    return function()
        for _, f in ipairs(t) do
            f()
        end
    end
end

function unit:fresh_cost()
    return function()
        for skl in self:each_skill() do
            skl:set_cost()
        end
    end
end

--获取单位的碰撞体积
function unit:get_collision()
    return self:get_slk('collision', 0)
end

--获取单位的选取半径
function unit:get_selected_radius()
    return self.selected_radius
end

--是否在指定位置附近(计算碰撞)
function unit:is_in_range(p, radius)
    return self:get_point() * p:get_point() - self:get_selected_radius() <= radius
end

--获得所有者
function unit:get_owner()
    return self.owner
end

-- 设置所有者
function unit:set_owner(p, color)
    jass.SetUnitOwner(self.handle, p.handle, not not color)
end
--endregion

--region 等级
--设置等级
--	等级
function unit:set_level(lv)
    if lv > self.level then
        jass.SetHeroLevel(self.handle, lv, self:is_hero())
    end
end

--获取等级
function unit:get_level()
    return self.level
end
--endregion

--region 技能(War3)
--添加技能
--	技能id
--	技能等级
function unit:add_ability(sid, lv)
    if not sid then
        return false
    end
    local id = base.string2id(sid)
    if not jass.UnitAddAbility(self.handle, id) then
        return false
    end
    if lv then
        jass.SetUnitAbilityLevel(self.handle, id, lv)
    end
    self:makePermanent(sid)
    return true
end

--移除技能
--	技能id
function unit:remove_ability(ability_id)
    if not ability_id then
        return false
    end
    local ability_id = base.string2id(ability_id)
    return jass.UnitRemoveAbility(self.handle, ability_id)
end

--允许技能
--	技能id
function unit:enable_ability(ability_id)
    self:get_owner():enable_ability(ability_id)
end

--禁用技能
--	技能id
function unit:disable_ability(ability_id)
    self:get_owner():disable_ability(ability_id)
end

--获取技能等级
--	技能id
function unit:getAbilityLevel(ability_id)
    local ability_id = base.string2id(ability_id)
    return jass.GetUnitAbilityLevel(self.handle, ability_id)
end

--设置技能等级
--	技能id
--	[技能等级]
function unit:setAbilityLevel(ability_id, lv)
    local ability_id = base.string2id(ability_id)
    jass.SetUnitAbilityLevel(self.handle, ability_id, lv or 1)
end

--命令单位使用技能
--	技能id
--	[目标]
function unit:castAbility(ability_id, target)
    local order = slk.ability[ability_id].Order
    if not target then
        return jass.IssueImmediateOrder(self.handle, order)
    elseif target.owner then
        return jass.IssueTargetOrder(self.handle, order, target.handle)
    else
        return jass.IssuePointOrder(self.handle, order, target:get_point():get())
    end
end

--设置技能永久性
--	技能id
function unit:makePermanent(ability_id)
    if not ability_id then
        return
    end
    local ability_id = base.string2id(ability_id)
    jass.UnitMakeAbilityPermanent(self.handle, true, ability_id)
end
--endregion

--region 技能(lua)
function unit:pause_skill(flag)
    if flag == nil then
        flag = true
    end
    if flag then
        self.pause_skill_count = self.pause_skill_count + 1
        if self.pause_skill_count == 1 then
            for skill in self:each_skill() do
                skill:pause(true)
            end
        end
    else
        if self.pause_skill_count == 0 then
            log.error '计数错误'
            return
        end
        self.pause_skill_count = self.pause_skill_count - 1
        if self.pause_skill_count == 0 then
            for skill in self:each_skill() do
                skill:pause(false)
            end
        end
    end
end

--判断是否暂停
function unit:is_pause_skill()
    return self.pause_skill_count > 0
end

--英雄添加技能
--	技能名
--	技能类型
--	[技能位置]
--	[初始数据]
--	@技能对象
function unit:add_skill(name, type, slotid, data)
    if not ac.skill[name] then
        log.error('技能不存在', name)
        return false
    end

    if not self.skills then
        self.skills = {}
    end

    if not self.skills[type] then
        self.skills[type] = {}
    end

    if not slotid then
        for i = 1, #self.skills[type] + 1 do
            if self.skills[type][i] == nil then
                slotid = i
                break
            end
        end
    end

    if self.skills[type][slotid] then
        log.error('该位置已有技能:', type, slotid, self.skills[type][slotid].name)
        return false
    end

    --print('添加技能:' .. name)
    if not data then
        data = {}
    end
	for k, v in pairs(ac.skill[name]) do
		if data[k] == nil then
			data[k] = v
		end
	end
	local skillType = require "ac.skill"
	local skill = setmetatable(data, skillType)
    skill.slot_type = type
	skill.slotid = slotid
	skill.owner = self
	self.skills[type][slotid] = skill

    skill.ability_id = skill.ability_id or self:get_owner():get_ability_id(type, slotid)

    if skill.cooldown_mode == 1 then
        skill.spell_stack = skill.charge_max_stack
    end
    if skill.passive then
        skill:set_option('passive', true)
    end

    skill:update_data()

    --每秒刷新技能
    if skill.auto_fresh_tip then
        ac.loop(1000, function(t)
            if skill.removed then
                t:remove()
                return
            end
            skill:fresh_tip()
        end)
    end

    if type == '物品' then
        ac.item.bind_item(skill)
    else
        if skill.ability_id then
            skill:add_ability()
            self:makePermanent(skill.ability_id)
            local order = skill:get_order()
            if order then
                if not self._order_skills then
                    self._order_skills = {}
                end
                if self._order_skills[order] then
                    log.error('技能指令冲突', order, self:get_name(), self._order_skills[order].name, skill.name, self._order_skills[order].ability_id, skill.ability_id)
                end
                self._order_skills[order] = skill
            end
        end
    end

    local lv = skill.level
    skill.level = 0
    skill:upgrade(lv)
    skill:fresh()

    return skill
end

--英雄移除技能
--	技能名
--	@是否成功
function unit:remove_skill(name)
    local skill = self:find_skill(name)
    if skill then
        return skill:remove()
    end
    return false
end

--从单位身上找技能
--	技能名称
--	[技能类型]
--	[是否包含未学习的英雄技能]
--	@技能对象
function unit:find_skill(name, skl_type, ignore_level)
    if not self.skills then
        return nil
    end
    if not skl_type then
        assert(type(name) == 'string', '按槽位号查找技能必须指定类型')
        for type in pairs(self.skills) do
            local skill = self:find_skill(name, type, ignore_level)
            if skill then
                return skill
            end
        end
        return nil
    end
    if not self.skills[skl_type] then
        return nil
    end
    for i, skill in pairs(self.skills[skl_type]) do
        if name == i or name == skill.name then
            if ignore_level or skill:get_level() > 0 then
                return skill
            end
        end
    end
    return nil
end

--遍历单位身上的技能
--	[技能类型]
--	[是否包含未学习的英雄技能]
--	@list
function unit:each_skill(type, ignore_level)
    if not self.skills then
        return function () end
    end
    local result = {}
    if type then
        if not self.skills[type] then
            return function () end
        end
        for _, v in pairs(self.skills[type]) do
            if ignore_level or v:get_level() > 0 then
                table_insert(result, v)
            end
        end
    else
        for _, type_skills in pairs(self.skills) do
            for _, v in pairs(type_skills) do
                if ignore_level or v:get_level() > 0 then
                    table_insert(result, v)
                end
            end
        end
    end
    local n = 0
    return function (t, v)
        n = n + 1
        return t[n]
    end, result
end

--打断施法
--	[效果来源]
function unit:cast_stop()
    if self:event_dispatch('单位-施法被打断', self) then
        return
    end
    local wait
    for _, skill in self:each_cast() do
        if not wait then
            wait = {}
        end
        table_insert(wait, skill)
    end
    if wait then
        for _, skill in ipairs(wait) do
            skill:stop()
        end
    end
end

--强制发动技能
--	技能名
--	[技能等级]
--	[技能目标]
function unit:cast_spell(name, level, target)
    local skl = self:find_skill(name, nil, true)
    if not skl then
        skl = self:add_skill(name, '隐藏')
    end
    if level then
        skl:set_level(level)
    end
    if skl:get_level() == 0 then
        --print('不能发动等级0的技能', name)
        return
    end
    if skl:is_cooling() then
        --print('正在冷却', name, skl:get_cast_cd())
        return
    end
    if skl:get_cost() > self:get '魔法' then
        --print('能量不足', name)
        return
    end
    --print('脚本发动技能', name)
    return skl:_cast_start {target = target, force_cast = 1, instant = 1}
end

--替换技能
--	被替换掉的技能名
--	替换上来的技能名
--	[是否继承冷却]
function unit:replace_skill(name, new_name, cool)
    --print('替换技能', name, new_name)
    local skl1 = self:find_skill(name, nil, true)
    local skl2 = self:find_skill(new_name, nil, true)
    if not skl1 then
        return false, '没有找到技能'
    end
    if not skl2 then
        skl2 = self:add_skill(new_name, '隐藏')
    end

    skl1:remove_ability()
    skl2:remove_ability()
    if not self._order_skills then
        self._order_skills = {}
    end
    for order, skill in pairs(self._order_skills) do
        if skill == skl1 then
            self._order_skills[order] = nil
        elseif skill == skl2 then
            self._order_skills[order] = nil
        end
    end

    skl1.ability_id, skl2.ability_id = skl2.ability_id, skl1.ability_id
    skl1.slot_type, skl2.slot_type = skl2.slot_type, skl1.slot_type
    skl1.slotid, skl2.slotid = skl2.slotid, skl1.slotid

    self.skills[skl1.slot_type][skl1.slotid] = skl1
    self.skills[skl2.slot_type][skl2.slotid] = skl2

    skl2:set_level(skl1:get_level())
    skl2:update_data()

    skl1:add_ability()
    skl2:add_ability()

    local order = skl1:get_order()
    if order then
        self._order_skills[order] = skl1
    end
    local order = skl2:get_order()
    if order then
        self._order_skills[order] = skl2
    end

    if cool then
        skl2.spell_stack = skl1.spell_stack
        skl2:set_cast_cd(skl1:get_cast_cd())
        skl2:set_charge_cd(skl1:get_charge_cd())
    end

    skl1:fresh()
    skl2:fresh()

    if skl1:is_ability_enable() then
        skl1:enable_ability()
    else
        skl1:disable_ability()
    end
    if skl2:is_ability_enable() then
        skl2:enable_ability()
    else
        skl2:disable_ability()
    end

    --刷新学习技能
    if skl1:get_type() == '英雄' then
        local lskl = self:find_skill(skl1.slotid, '学习')
        if lskl then
            lskl:_call_event 'on_add'
        end
    end
    if skl2:get_type() == '英雄' then
        local lskl = self:find_skill(skl2.slotid, '学习')
        if lskl then
            lskl:_call_event 'on_add'
        end
    end
    return true
end

-- 获取正在施放的技能
function unit:each_cast(name)
    local skills = self._casting_list
    local t = {}
    if skills then
        for _, skill in ipairs(skills) do
            if not name or name == skill.name then
                --print(skill.name)
                table_insert(t, skill)
            end
        end
    end
    return ipairs(t)
end

-- 寻找正在施放的技能
function unit:find_cast(name)
    local skills = self._casting_list
    if skills then
        for _, skill in ipairs(skills) do
            if not name or name == skill.name then
                return skill
            end
        end
    end
    return nil
end

-- 命令使用技能
function unit:cast(name, target, data)
    local skill = self:find_skill(name)
    if not skill then
        return false
    end
    return skill:cast(target, data)
end

-- 命令强制使用技能
function unit:force_cast(name, target, data)
    local skill = self:find_skill(name)
    if not skill then
        return false
    end
    data = data or {}
    data.force_cast = 1
    return skill:cast(target, data)
end

function unit:show_fresh()
    self:add_ability 'A888'
    self:remove_ability 'A888'
end
--endregion

--region 命令
--发布命令
--	命令
--	[目标]
function unit:issue_order(order, target)
    local res
    self.script_order = true
    if not target then
        res = jass.IssueImmediateOrder(self.handle, order)
    elseif target.owner then
        res = jass.IssueTargetOrder(self.handle, order, target.handle)
    else
        local x, y
        if target.type == 'point' then
            x, y = target:get()
        else
            x, y = target:get_point():get()
        end
        res = jass.IssuePointOrder(self.handle, order, x, y)
    end
    self.script_order = false
    return res
end

--获得命令
--	@命令
function unit:getOrder()
    local order = jass.GetUnitCurrentOrder(self.handle)
    return id2order[order], order
end
--endregion

--region 计时器
--暂停单位计时器
function unit:pause_timer(flag)
	if not flag then flag = true end

	if flag then
		self.pause_timer_count = self.pause_timer_count + 1
		if self.pause_timer_count == 1 and self._timers then
			for _, t in ipairs(self._timers) do
				t:pause()
			end
		end
	else
		if self.pause_timer_count == 0 then
			log.error '计数错误'
			return
		end
		self.pause_timer_count = self.pause_timer_count - 1
		if self.pause_timer_count == 0 and self._timers then
			for _, t in ipairs(self._timers) do
				t:resume()
			end
		end
	end
end

function unit:is_pause_timer()
	return self.pause_timer_count > 0
end
--endregion

--region buff
function unit:pause_buff(flag)
    if flag == nil then
        flag = true
    end
    if flag then
        self.pause_buff_count = self.pause_buff_count + 1
        if self.pause_buff_count == 1 then
            if self.buffs then
                for buff in pairs(self.buffs) do
                    buff:pause(true)
                end
            end
        end
    else
        if self.pause_buff_count == 0 then
            log.error '计数错误'
            return
        end
        self.pause_buff_count = self.pause_buff_count - 1
        if self.pause_buff_count == 0 then
            if self.buffs then
                for buff in pairs(self.buffs) do
                    buff:pause(false)
                end
            end
        end
    end
end

--判断是否暂停
function unit:is_pause_buff()
    return self.pause_buff_count > 0
end

--添加buff
function unit:add_buff(name, delay)
	return function(bff)
		local data = ac.buff[name]
		if not data then
			log.error('未找到buff', name)
			return
		end
		gchash = gchash + 1
		dbg.gchash(bff, gchash)
		bff.gchash = gchash
		setmetatable(bff, data)
		if not self.buffs then
			self.buffs = {}
		end

		--初始化数据
		bff.name = bff.name or name
		bff.target = self
		bff.target = self
		if not bff.source then
			bff.source = self
		end
		if delay then
			ac.timer(delay * 1000, 1, function()
				if bff.removed then
					return
				end
				bff:add()
			end)
			return bff
		else
			return bff:add()
		end
	end
end


function unit:each_buff(name)
	if not self.buffs then
		return function () end
	end
	local buffs = {}
	for buff in pairs(self.buffs) do
		buffs[buff] = true
	end
	if not name then
		return pairs(buffs)
	end
	local next, s, var = pairs(buffs)
	return function (s, var)
		while true do
			local r = next(s, var)
			if not r then return nil end
			if r.name == name then return r end
			var = r
		end
	end, s, var
end

--移除buff
function unit:remove_buff(name)
	if not self.buffs then
		return
	end
	local tbl = {}
	for buff in pairs(self.buffs) do
		if buff.name == name then
			tbl[#tbl + 1] = buff
		end
	end
	for i = 1, #tbl do
		tbl[i]:remove()
	end
end

--找buff
function unit:find_buff(name)
	if not self.buffs then
		return
	end
	for buff in pairs(self.buffs) do
		if buff.name == name then
			return buff
		end
	end
end
--endregion

--region 移动
function unit:pause_mover(flag)
    if flag == nil then
        flag = true
    end
    if flag then
        self.pause_mover_count = self.pause_mover_count + 1
        if self.pause_mover_count == 1 and self.movers then
            for mover in pairs(self.movers) do
                mover:pause(true)
            end
        end
    else
        if self.pause_mover_count == 0 then
            log.error '计数错误'
            return
        end
        self.pause_mover_count = self.pause_mover_count - 1
        if self.pause_mover_count == 0 and self.movers then
            for mover in pairs(self.movers) do
                mover:pause(false)
            end
        end
    end
end

--判断是否暂停
function unit:is_pause_mover()
    return self.pause_mover_count > 0
end
--endregion

--region 颜色
--设置单位颜色
--	[红(%)]
--	[绿(%)]
--	[蓝(%)]
function unit:setColor(red, green, blue)
    self.red, self.green, self.blue = red, green, blue
    jass.SetUnitVertexColor(
        self.handle,
        self.red * 2.55,
        self.green * 2.55,
        self.blue * 2.55,
        self.alpha * 2.55
    )
end

--设置单位透明度
--	透明度(%)
function unit:setAlpha(alpha)
    self.alpha = alpha
    jass.SetUnitVertexColor(
        self.handle,
        self.red * 2.55,
        self.green * 2.55,
        self.blue * 2.55,
        self.alpha * 2.55
    )
end

--获取单位透明度
function unit:getAlpha()
    return self.alpha
end
--endregion

--region 动画
--设置单位动画/动画名或动画序号
function unit:set_animation(ani)
    if not self:is_alive() then
        return
    end
    if type(ani) == 'string' then
        jass.SetUnitAnimation(self.handle, self.animation_properties .. ani)
    else
        jass.SetUnitAnimationByIndex(self.handle, ani)
    end
end

--将动画添加到队列
--	动画序号
function unit:add_animation(ani)
    if not self:is_alive() then
        return
    end
    jass.QueueUnitAnimation(self.handle, ani)
end

--设置动画播放速度
--	速度
function unit:set_animation_speed(speed)
    jass.SetUnitTimeScale(self.handle, speed)
end

--添加动画附加名
--	附加名
function unit:add_animation_properties(name)
    jass.AddUnitAnimationProperties(self.handle, name, true)
    self.animation_properties = self.animation_properties .. name .. ' '
end

--移除动画附加名
--	附加名
function unit:remove_animation_properties(name)
    jass.AddUnitAnimationProperties(self.handle, name, false)
    self.animation_properties = self.animation_properties:gsub(name .. ' ', '')
end
--endregion

--region 视野
--是否可见
--	对象
function unit:is_visible(dest)
    if dest.type ~= 'player' then
        dest = dest:get_owner()
    end
    return jass.IsUnitVisible(self.handle, dest.handle)
end

--设置索敌范围
function unit:set_search_range(r)
    jass.SetUnitAcquireRange(self.handle, r)
end

--添加单位视野(依然不能超过1800)
function unit:addSight(r)
    self:add_ability 'A007'
    local handle = japi.EXGetUnitAbility(self.handle, base.string2id 'A007')
    japi.EXSetAbilityDataReal(handle, 2, 108, - r)
    self:setAbilityLevel('A007', 2)
    self:remove_ability 'A007'
end

--共享视野
function unit:shareVisible(p, flag)
    jass.UnitShareVision(self.handle, p.handle, flag ~= false and true or false)
end
--endregion

--region 特效
--创建特效
--	附加点
--	模型路径
function unit:add_effect(part, model)
    local j_eff = jass.AddSpecialEffectTarget(model, self.handle, part)
    dbg.handle_ref(j_eff)
    local eff = effect.new()
    eff.handle = j_eff
    eff.model = model
    eff.unit = self
    eff.socket = part

    --存在单位身上
    if not self._effect_list then
        self._effect_list = {}
    end
    table.insert(self._effect_list, eff)

    return eff
end

function unit:effect(data)
    return ac.unit_effect(self, data)
end

--移除所有特效
function unit:removeAllEffects()
    if self._effect_list then
        for i, eff in ipairs(self._effect_list) do
            jass.DestroyEffect(eff.handle)
            dbg.handle_unref(eff.handle)
            eff.handle = nil
            eff.removed = true
        end
        self._effect_list = nil
    end
end
--endregion

--region 创建单位(以单位为参照)
--	单位id
--	[位置(默认为参照单位)]
--	朝向
function unit:create_unit(id, where, face)
    if not where then
        where = self:get_point()
    end
    return g_unitMgr:create_unit(self:get_owner(), id, where, face or self:get_facing())
end

--转换handle为单位
function unit.j_unit(handle)
    return g_unitMgr:j_unit(handle)
end

function unit:__call(handle)
    return self.j_unit(handle)
end

--endregion

--region 马甲/幻像
--创建马甲
--	位置
--	朝向
function unit:create_dummy(id, where, face)
	local u = self:get_owner():create_dummy(id or self:get_type_id(), where, face)
	return u
end

--创建镜像
--	位置
--	朝向
--	不复制物品
function unit:create_illusion(p, no_item)
	local life = self:get '生命'
	self:set('生命', self:get '生命上限')
	ignore_flag = true
	jass.IssueTargetOrderById(ac.dummy.handle, 852274, self.handle)
	ignore_flag = false
	self:set('生命', life)
	if not last_summoned_unit then
		return
	end
	local handle = last_summoned_unit
	if not handle then
		return
	end
	dbg.handle_ref(handle)
	last_summoned_unit = nil
	jass.SetUnitOwner(handle, self:get_owner().handle, false)
	local dummy = unit.new(handle, self:get_owner())
	jass.SetUnitBlendTime(handle, dummy:get_slk('blend', 0))
	dummy:set_position(p, true, true)
	setmetatable(dummy, getmetatable(self))
	dummy.unit_type = self.unit_type
	dummy._is_illusion = true
	dummy:set_class(self:get_class())
	dummy.hero_data = self.hero_data
	if dummy:getAbilityLevel 'Aloc' == 0 then
		dummy:event_notify('单位-创建', dummy)
	end

	for k, v in pairs(self.hero_data.attribute) do
		dummy:set(k, v)
	end
	
	--复制等级
	for i = 1 + 1, self:get_level() do
		dummy:event_dispatch('单位-英雄升级', dummy)
	end

	--复制技能
	for skl in self:each_skill() do
		if not skl.never_copy then
			local skl2 = dummy:add_skill(skl.name, skl:get_type(), skl:get_slotid())
			skl2:set_level(skl:get_level(), false)
		end
	end

	--敌我识别标记
	dummy:add_enemy_tag()
	--当前生命值与魔法值
	dummy:set('生命', life)
	dummy:set('魔法', self:get '魔法')
	
	return dummy
end
--endregion

--region 伤	害
--造成伤害
function unit:damage(data)
    data.target = self
    setmetatable(data, damage)
    return data()
end

--进行治疗
function unit:heal(data)
    data.target = self
	local heal = ac.heal.new()
	return heal(data)
end

--发动一次物理伤害
function unit:attackDamage(data)
    data.attack = true
    data.common_attack = true
    data.skill = data.skill or false
    setmetatable(data, damage)
    return data()
end

--增加伤害倍率
function unit:addDamageRate(r)
	self.damage_rate = self.damage_rate + r
	if self.freshDamageInfo then
		self:freshDamageInfo()
	end
end

--获取伤害倍率
function unit:getDamageRate()
	return self.damage_rate
end

--增加受伤倍率
function unit:addDamagedRate(r)
	self.damaged_rate = self.damaged_rate + r
	if self.freshDefenceInfo then
		self:freshDefenceInfo()
	end
end

--获取受伤倍率
function unit:getDamagedRate()
	return self.damaged_rate
end
--endregion

--region 战斗状态
--设置战斗状态
function unit:setActive(dest)
	self.last_active_time = self:clock()
	if not self.active then
		self.active = true
		self:event_notify('单位-进入战斗', self, dest)
	end
end

function unit:updateActive()
	if self.active then 
		if not self:isActive() then
			self.active = false
			self:event_notify('单位-脱离战斗', self)
		end
	end
end

--单位是否处于战斗状态
function unit:isActive()
	return self:clock() - self.last_active_time < 3000
end
--endregion

--region 消耗
--获取在节能施法后的耗蓝
--	原来的耗蓝
function unit:get_cost(mana)
	if mana <= 0 then
		return mana
	end
	local resource = ac.resource[self.resource_type]
	return mana - mana * self:get '减耗' / 100 * resource.get_cost_save_rate
end

--消耗法力
--	法力
--	@是否消耗成功
function unit:cost_mana(mana)
	local mana = self:get '魔法' - self:get_cost(mana)
	if mana < 0 then
		return false
	end
	if not ac.wtf then
		self:set('魔法', mana)
	end
	return true
end
--endregion

--region 攻击
function unit:get_launch_point()
	if not self.launch_z then
		local weapon_launch = self.weapon and self.weapon['弹道出手']
		local x = weapon_launch and weapon_launch[1] or self:get_slk('launchX', 0)
		local y = weapon_launch and weapon_launch[2] or self:get_slk('launchY', 0)
		self.launch_z = weapon_launch and weapon_launch[3] or self:get_slk('launchZ', 0)
		self.launch_distance = math.sqrt(x * x + y * y)
		self.launch_angle = math.atan(y, x)
	end
	local p = self:get_point()
	local size = self:get_size()
	local angle = self:get_facing()
	local point = p - {angle + self.launch_angle, size * self.launch_distance}
	point[3] = size * self.launch_z
	return point
end

function unit:melee_attack_start(data)
	self:attackDamage(data)
end

function unit:range_attack_start(data)
	--发射一个弹道
	local target = data.target
	local size = self:get_size()
	local start = self:get_launch_point()
	local speed = self.missile_speed or (self.weapon and self.weapon['弹道速度']) or self:get_slk('Missilespeed_1', 0)
	local arc = self.weapon and self.weapon['弹道弧度'] or self:get_slk('Missilearc_1', 0)
	local mover_data = ac.mover.target
	{
		source = self,
		start = start,
		target = target,
		path = true,
		speed = speed,
		model = self.missile_art or (self.weapon and self.weapon['弹道模型']) or self:get_slk 'Missileart_1',
		height = self:get_point() * target:get_point() * arc,
		damage = data.damage,
		size = size,
		skill = false,
		need_elevation = self:is_type('英雄'),
	}

	--弹道击中目标时造成伤害
	function mover_data:on_finish()
		if self.target:is_alive() then
			data.missile = mover_data.mover
			self.source:attackDamage(data)
		end
	end

	if speed <= 0 then
		mover_data.mover:set_position(target:get_point(), true)
		mover_data.mover:set_high(mover_data.target_high)
		mover_data:on_finish()
		mover_data:remove()
	end
end

--进行一次攻击
function unit:attack_start(target, skill, attack_damage)
	if target:has_restriction '物免' then
		return false
	end
	local dmg = attack_damage or self:get '攻击'
	local data = self.last_attack_damage
	if skill or attack_damage or not data then
		data = {
			source = self,
			target = target,
			attack = true,
			common_attack = true,
			damage = dmg,
			skill = skill or false,
		}
		setmetatable(data, damage)
	end
	self.last_attack_damage = nil

	if self:event_dispatch('单位-发动攻击', data) then
		return false
	end
	
	data.source:event_notify('单位-攻击出手', data)
	data.target:event_notify('单位-被攻击出手', data)

	--如果是近战攻击,则直接造成伤害
	if self:isMelee() then
		self:melee_attack_start(data)
	else
		self:range_attack_start(data)
	end
	return true
end
--endregion

--region 自定义类型(马甲/幻象)
function unit:get_class()
	return self._class
end

function unit:set_class(class)
	if self._class == class then
		return
	end
	if class == '幻象' then
		if self._class ~= '马甲' then
			self:add_ability 'Aloc'
			self:add_restriction '无敌'
		end
		self._class = '幻象'
		-- 显示血条
		jass.ShowUnit(self.handle, false)
		jass.ShowUnit(self.handle, true)
		self._class_timer = loop(10, function()
			jass.SelectUnit(self.handle, false)
		end)
	elseif class == '马甲' then
		if self._class == '幻象' then
			if self._class_timer then
				self._class_timer:remove()
			end
			self:remove_ability 'Aloc'
			self:add_ability 'Aloc'
		else
			self:add_ability 'Aloc'
			self:add_restriction '无敌'
		end
		self._class = '马甲'
	end
end
--endregion

--region 行为限制
local function restriction_move(unit, flag)
	if flag then
		jass.SetUnitMoveSpeed(unit.handle, 0)
	else
		jass.SetUnitMoveSpeed(unit.handle, unit:get '移动速度')
	end
end

local function restriction_attack(unit, flag)
	if flag then
		unit:add_ability 'Abun'
	else
		unit:remove_ability 'Abun'
	end
end

local function restriction_attacked(unit, flag)
	if flag then
		jass.UnitAddType(unit.handle, jass.UNIT_TYPE_ANCIENT)
	else
		jass.UnitRemoveType(unit.handle, jass.UNIT_TYPE_ANCIENT)
	end
end

local function restriction_spell(self, flag)
	for skill in self:each_skill() do
		if skill:is_visible() and not skill.passive and (skill:get_type() == '英雄' or skill:get_type() == '通用') then
			skill:fresh_art()
		end
	end
	self:show_fresh()
end

local function restriction_spelled(unit, flag)
end

local function restriction_dead(unit, flag)
end

local function restriction_stealth(unit, flag)
	if flag then
		unit:add_ability 'A00E'
	else
		unit:remove_ability 'A00E'
	end
end

local function restriction_hide(unit, flag)
	jass.ShowUnit(unit.handle, not flag)
end

local function restriction_akari(unit, flag)
	if flag then
		jass.SetUnitFlyHeight(unit.handle, 999999, 0)
	else
		jass.SetUnitFlyHeight(unit.handle, unit.high, 0)
	end
end

local function restriction_fly(unit, flag)
    if flag then
        japi.EXSetUnitMoveType(unit.handle, 4)
    else
    	if unit:has_restriction '幽灵' then
        	japi.EXSetUnitMoveType(unit.handle, 16)
		else
        	japi.EXSetUnitMoveType(unit.handle, 2)
    	end
    end
end

local function restriction_collision(unit, flag)
    if unit:has_restriction '飞行' then
        return
    end
	if flag then
        japi.EXSetUnitMoveType(unit.handle, 16)
	else
        japi.EXSetUnitMoveType(unit.handle, 2)
	end
end

local function restriction_stun(self, flag)
	if flag then
		self:add_restriction '硬直'
	else
		self:remove_restriction '硬直'
	end
end

local function restriction_time_pause(self, flag)
	if flag then
		self.last_pause_clock = ac.clock()
		self:add_restriction '硬直'
		self:pause_buff(true)
		self:pause_skill(true)
		self:pause_mover(true)
		self:pause_timer(true)
		self:setAlpha(self:getAlpha() - 50)
		ac.wait(0, function()
			self:set_animation_speed(0)
		end)
		self:set_facing(self:get_facing())
		self:event_notify('单位-时停开始', self)
	else
		self.paused_clock = self.paused_clock + ac.clock() - self.last_pause_clock
		self:setAlpha(self:getAlpha() + 50)
		self:set_animation_speed(1)
		self:pause_timer(false)
		self:pause_mover(false)
		self:pause_skill(false)
		self:pause_buff(false)
		self:remove_restriction '硬直'
		self:event_notify('单位-时停结束', self)
	end
end

local function restriction_hard(self, flag)
	if flag then
		if not self._ignore_order_list then
			self._ignore_order_list = {}
		end
		local order = self._current_issue_order
		if order and self._order_skills and self._order_skills[order] then
			table.insert(self._ignore_order_list, order)
		end
		japi.EXPauseUnit(self.handle, true)
	else
		japi.EXPauseUnit(self.handle, false)
		if self._recover_skill then
			local skill = self._recover_skill
			self._recover_skill = nil
			skill[1]:cast_by_client(skill[2])
		end
	end
end

local function restriction_god(self, flag)
	if flag then
		self:add_restriction '物免'
		self:add_restriction '魔免'
	else
		self:remove_restriction '物免'
		self:remove_restriction '魔免'
	end
end

local function restriction_constraint()
end

local restriction_type = {
	['定身']	= restriction_move,
	['缴械']	= restriction_attack,
	['物免']	= restriction_attacked,
	['禁魔']	= restriction_spell,
	['魔免']	= restriction_spelled,
	['免死']	= restriction_dead,
	['隐身']	= restriction_stealth,
	['隐藏']	= restriction_hide,
	['阿卡林']	= restriction_akari,
	['飞行']	= restriction_fly,
	['幽灵']	= restriction_collision,
	['蝗虫']	= false,
	['晕眩']	= restriction_stun,
	['时停']	= restriction_time_pause,
	['硬直']	= restriction_hard,
	['无敌']	= restriction_god,
	['禁锢']	= restriction_constraint,
}

function unit:add_restriction(name)
	if not restriction_type[name] then
		log.error('错误的限制类型', name)
		return 0
	end
	local res = self['限制']
	if not res then
		res = {}
		self['限制'] = res
	end
	res[name] = (res[name] or 0) + 1
	if res[name] == 1 and restriction_type[name] then
		restriction_type[name](self, true)
	end
	return res[name]
end

function unit:remove_restriction(name)
	if not restriction_type[name] then
		log.error('错误的限制类型', name)
		return 0
	end
	local res = self['限制']
	if not res then
		res = {}
		self['限制'] = res
	end
	res[name] = (res[name] or 0) - 1
	if res[name] == 0 and restriction_type[name] then
		restriction_type[name](self, false)
	end
	if res[name] == -1 then
		log.error('计数错误', name)
	end
	return res[name]
end

function unit:has_restriction(name)
	if not restriction_type[name] then
		log.error('错误的限制类型', name)
		return false
	end
	local res = self['限制']
	if not res then
		res = {}
		self['限制'] = res
	end
	return res[name] and res[name] > 0
end

function unit:event(name)
	return ac.event_register(self, name)
end
--endregion

--region 其他
function unit:clock()
    if self:has_restriction '时停' then
        return self.last_pause_clock - self.paused_clock
    else
        return ac.clock() - self.paused_clock
    end
end

function unit:follow(data)
    data.target = self
    return ac.follow(data)
end

--获得金钱
--	金钱数量
--	[显示位置]
--	[不抛出加钱事件]
function unit:addGold(gold, where, flag)
    self:get_owner():addGold(gold, where or self, flag)
end

function unit:add_enemy_tag()
    if self.enemy_tag then
        self.enemy_tag:remove()
    end
    --敌我识别特效
    local str
    if self:is_ally(g_playerMgr.self) then
        str = [[modeldekan\ui\DEKAN_Tag_Ally.mdl]]
    else
        str = [[modeldekan\ui\DEKAN_Tag_Enmy.mdl]]
    end
    self.enemy_tag = self:add_effect('origin', str)
end

--更新数据
function unit:update()
    if self:has_restriction '时停' then
        return
    end
    local life_recover, life_recover_idle = self:get '生命恢复', self:get '生命脱战恢复'
    local mana_recover, mana_recover_idle = self:get '魔法恢复', self:get '魔法脱战恢复'
    if not self.active then
        life_recover = life_recover + life_recover_idle
        mana_recover = mana_recover + mana_recover_idle
    end
    if life_recover ~= 0 then
        self:add('生命', life_recover / g_unitMgr.frame)
    end
    if mana_recover ~= 0 then
        self:add('魔法', mana_recover / g_unitMgr.frame)
    end
end
--endregion

ac.unit = unit

return unit
